/*
 * jconfig.cc
 *
 *  Created on: Dec 2, 2010
 *      Author: fify
 */

#include "jconfig.h"
#include "utils.h"
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <stdio.h>

using std::string;
using std::vector;
using std::ifstream;

namespace CncrJConfig
{

JConfig::JConfig()
{
	m_status = STATUS_NONE;
}

JConfig::~JConfig()
{
	if(m_status != STATUS_NONE)
		enter_status("", "", STATUS_LOGIN);
}

int JConfig::init(string host, int port)
{
	//cout << "Init()" << endl;
	if(m_sock.conn_jos(host, port) < 0)
	{
		m_err_msg = "Connecting to JunOS error!";
		return -1;
	}

	return 0;
}

void JConfig::perror(const char* msg)
{
	std::cout << msg << ": " << m_err_msg << std::endl;
}

int JConfig::try_login(string user, string passwd)
{
	if(m_status != STATUS_LOGIN)
	{
		m_err_msg = "Current status error: STATUS_LOGIN needed!";
		return -1;
	}

#ifdef DEBUG
	printf("Logging in with %s %s\n", user.c_str(), passwd.c_str());
#endif
	vector<string> result;

	if(m_sock.send_msg(user) < 0)
	{
		m_err_msg = "Send message error: " + user;
		return -1;
	}
	//m_sock.recv_all(result);

	if(m_sock.expect("Password:") < 0)
	{
		m_err_msg = "Error while trying to login.";
		return -1;
	}

	if(m_sock.send_msg(passwd) < 0)
	{
		m_err_msg = "Send message error: " + passwd;
		return -1;
	}
/*
	if(m_sock.expect("Login incorrect", 50) == 0)
	{
		m_err_msg = "Login failed: user name or password error!";
		return -1;
	}*/

	if(update_status() < 0)
	{
		this->perror("try_login error");
		return -1;
	}

	if(m_status != STATUS_CLI && m_status != STATUS_ROOT)
	{
		m_err_msg = "Trying to login error!";
		return -1;
	}

	return 0;
}

int JConfig::enter_cli(string user, string passwd)
{
	if(m_status == STATUS_LOGIN && try_login(user, passwd) < 0)
		return -1;

	vector<string> result;
	if(m_status == STATUS_ROOT)
	{
		if(m_sock.send_msg("cli") < 0)
		{
			m_err_msg = "Send message error: cli";
			return -1;
		}
		if(m_sock.expect(">") < 0)
		{
			m_err_msg = "Error while entering cli";
			return -1;
		}
	}
	else
	{
		m_err_msg = "Current status error: STATUS_LOGIN or STATUS_ROOT needed!";
		return -1;
	}

	if(update_status() < 0)
	{
		this->perror("enter_cli error");
		return -1;
	}

	if(m_status != STATUS_CLI)
	{
		m_err_msg = "Entering CLI error!";
		return -1;
	}

	return 0;
}

int JConfig::enter_status(string user, string passwd, JStatus status)
{
#ifdef DEBUG
	printf("\033[33m" "enter_status (%d->%d)\n" "\033[0m", m_status, status);
#endif
	if(update_status() < 0)
		return -1;

	if(status == STATUS_ROOT && user != "root")
	{
		m_err_msg = "Normal user cannot enter Root Mode!";
		return -1;
	}

	while(m_status != status)
	{
		if(m_status > status)
		{
			this->exit();
			continue;
		}

		switch(m_status)
		{
		case STATUS_LOGIN:
			if(try_login(user, passwd) < 0)
			return -1;
			break;
		case STATUS_ROOT:
			if(enter_cli(user, passwd) < 0)
			return -1;
			break;
		case STATUS_CLI:
			if(enter_config() < 0)
			return -1;
			break;
		case STATUS_CONFIG:
			break;
		default:
			m_err_msg = "Unknown status specified.";
			return -1;
		}
	}

	//printf("%d\n", m_status);
	return 0;
}

int JConfig::enter_config()
{
	if(m_status != STATUS_CLI)
	{
		m_err_msg = "Current status error: STATUS_CLI needed!";
		return -1;
	}

	if(m_sock.send_msg("configure") < 0)
	{
		m_err_msg = "Send message error: configure";
		return -1;
	}

	vector<string> exp_vec;
	exp_vec.push_back("The configuration has been changed but not committed");
	exp_vec.push_back("Entering configuration mode");

	int temp = m_sock.expect(exp_vec);
	if(temp == 0)
	{
		if(m_sock.send_msg("rollback") < 0)
		{
			m_err_msg = "Send message error: rollback";
			return -1;
		}
#ifdef DEBUG
		m_sock.print_and_clear();
#else
		//m_sock.recv_and_clear();
#endif
	}
	else if(temp == 1)
	{
		m_status = STATUS_CONFIG;
	}
	else
	{
		if(update_status() < 0)
		{
			this->perror("enter_config error");
			return -1;
		}
	}

	if(m_status != STATUS_CONFIG)
	{
		m_err_msg = "Error in entering configuration mode.";
		return -1;
	}

	return 0;
}

int JConfig::configure(string cmd)
{
	if(m_status != STATUS_CONFIG)
	{
		m_err_msg = "Current status error: STATUS_CLI needed!";
		return -1;
	}
#ifdef DEBUG
	std::cout << "Executing: "<< cmd << std::endl;
#endif
	if(m_sock.send_msg(cmd) < 0)
	{
		m_err_msg = "Send message error: " + cmd;
	}
	if(m_sock.expect("[edit]") != 0)
	{
		m_err_msg = "Unknown error in JConfig::configure";
		return -1;
	}
#ifdef DEBUG
	m_sock.print_and_clear();
#else
	//m_sock.recv_and_clear();
#endif

	return 0;
}

int JConfig::load_cmds(vector<string> &cmds)
{
	if(m_status != STATUS_CONFIG)
	{
		m_err_msg = "Current status error: STATUS_CLI needed!";
		return -1;
	}

	vector<string> exp_list;
	exp_list.push_back("syntax error.");
	exp_list.push_back("[edit]");

	for(int i = 0;i<(int)cmds.size();i++)
	{
		if(m_sock.send_msg(cmds[i]) < 0)
		{
			m_err_msg = "Send message error: " + cmds[i];
		}
		if(m_sock.expect(exp_list) == 0)
		{
			m_err_msg = "Syntax Error: " + cmds[i];
			return -1;
		}
	}

	return check_and_commit();
}

int JConfig::load_config_file(string filename)
{
	if(m_status != STATUS_CONFIG)
	{
		m_err_msg = "Current status error: STATUS_CLI needed!";
		return -1;
	}

	if(m_sock.send_msg("load merge terminal") < 0)
	{
		m_err_msg = string("Send message error: ") + string("load merge terminal");
		return -1;
	}

	if(m_sock.expect("[Type ^D at a new line to end input]") < 0)
	{
		m_err_msg = "Unknown error in load_config_file";
		return -1;
	}

	ifstream ifs;
	ifs.open(filename.c_str());
	string buf;
	while(ifs >> buf)
	{
		if(m_sock.send_msg(buf) < 0)
		{
			m_err_msg = string("Send message error: ") + string("buf");
			return -1;
		}
	}

	if(m_sock.send_msg("\004") < 0)
	{
		m_err_msg = string("Send message error: ") + string("Ctrl + D");
	}

	return check_and_commit();
}

int JConfig::check_and_commit()
{
	if(m_status != STATUS_CONFIG)
	{
		m_err_msg = "Current status error: STATUS_CONFIG needed!";
		return -1;
	}

	if(m_sock.send_msg("commit check") < 0)
	{
		m_err_msg = string("Send message error: ") + string("commit check");
		return -1;
	}
	vector<string> exp_vec;
	exp_vec.push_back("configuration check succeeds");
	exp_vec.push_back("error: configuration check-out failed");
	int temp = m_sock.expect(exp_vec);
	if(temp == 1)
	{
		m_err_msg = "Commit check failed.";
		return -1;
	}
	else if(temp == 0)
	{
		if(m_sock.send_msg("commit") < 0)
		{
			m_err_msg = string("Send message error: ") + string("commit");
			return -1;
		}
	}
	else
	{
		m_err_msg = "Unknow error in check_and_commit";
		return -1;
	}

	if(m_sock.expect("commit complete") != 0)
	{
		m_err_msg = "Error while committing.";
		return -1;
	}

	return 0;
}

int JConfig::exit(int force)
{
#ifdef DEBUG
	printf("\033[32m" "Exiting (%d)\n" "\033[0m", m_status);
#endif
	if(update_status() < 0)
	{
		return -1;
	}

	vector<string> exp_list;

	switch(m_status)
	{
	case STATUS_LOGIN:
		return 0;
	case STATUS_ROOT:
	case STATUS_CLI:
		if(m_sock.send_msg("exit") < 0)
		{
			m_err_msg = string("Send message error: ") + "exit";
			return -1;
		}
#ifdef DEBUG
		m_sock.print_and_clear();
#else
		//m_sock.recv_and_clear();
#endif
		break;
	case STATUS_CONFIG:
		exp_list.clear();
		exp_list.push_back("Exiting configuration mode");
		exp_list.push_back("Exit with uncommitted changes?");

		while(1)
		{
			if(m_sock.send_msg("quit") < 0)
			{
				m_err_msg = string("Send message error: ") + "quit";
				return -1;
			}
			int exp_ret = m_sock.expect(exp_list);

			if(exp_ret == 0)
				break;
			else if(exp_ret == 1)
			{
				if(m_sock.send_msg("no") < 0)
				{
					m_err_msg = string("Send message error: ") + "no";
					return -1;
				}
#ifdef DEBUG
				m_sock.print_and_clear();
#else
				//m_sock.recv_and_clear();
#endif
				if(m_sock.send_msg("rollback") < 0)
				{
					m_err_msg = string("Send message error: ") + "rollback";
					return -1;
				}
#ifdef DEBUG
				m_sock.print_and_clear();
#else
				//m_sock.recv_and_clear();
#endif
			}
		}

		break;
	default:
		return -1;
	}

	if(update_status() < 0)
	{
		this->perror("enter status error in exit()");
		return -1;
	}

#ifdef DEBUG
	printf("\033[32m" "Exited (%d)\n" "\033[0m", m_status);
#endif

	return 0;
}

int JConfig::junos_started()
{
	return update_status() >= 0 && m_status != STATUS_NONE;
}

// This function always returns 0.
int JConfig::update_status()
{
#ifdef DEBUG
	printf("\033[31m" "Update status (%d)\n" "\033[0m", m_status);
#endif

	if(m_sock.send_msg("") < 0)
	{
		m_err_msg = string("Send message error") + "\\n";
		return -1;
	}

	vector<string> exp_list;
	exp_list.push_back("login:");
	exp_list.push_back("Password:");
	exp_list.push_back("@olive-9.6>");
	exp_list.push_back("root@olive-9%");
	exp_list.push_back("@olive-9.6#");

	int flag = 0;
	while(1)
	{
		int exp_ret = m_sock.expect(exp_list);
		flag = 1;

		switch(exp_ret)
		{
		case 0:	// login:
			m_status = STATUS_LOGIN;
			break;
		case 1:	// Password:
			if(m_sock.send_msg("") < 0)
			{
				m_err_msg = string("Send message error: ") + "\\n";
				return -1;
			}
			flag = 0;
#ifdef DEBUG
			m_sock.print_and_clear();
#else
			//m_sock.recv_and_clear();
#endif
			break;
		case 2:	// >
			m_status = STATUS_CLI;
			break;
		case 3:	// %
			m_status = STATUS_ROOT;
			break;
		case 4:	// #
			m_status = STATUS_CONFIG;
			break;
		default:
			m_status = STATUS_NONE;
			m_err_msg = "Unknown error in update_status(). Maybe the Olive is still not started?";
		}
		if(flag)
			break;
	}

#ifdef DEBUG
	printf("\033[31m" "Status updated: %d\n" "\033[0m", m_status);
#endif

	return 0;
}

} // End of namespace.
